Studbook Overview

Let’s create a graphical table to summarize our studbook data.

studbook.tbl <- studbook %>% studbook_react(., studbook.cols(.), groupBy = "Status")
studbook.tbl

Life Table Calculations

Now we will use annual birth counts to follow the standard method for computing a normalized rate of growth over the past 5 years compared to all years.

lifeTab      <- cohort_lifeTab(timeline, studbook, 1985, 2024, 5, 22)

And then we will plot trends in this growth rate based on birth year.

lambda.plot  <- plot_lambda(lifeTab, colors, "Population Growth Rates by Cohort")
lambda.plot

Construct Pedigree

Now we will use a package called pedtools to create our pedigree objects based on the studbook data. We will use the pedigree object to generate all genetic stats and projections.

pedig <- ped(
  id     = studbook$ID,
  fid    = studbook$Sire,
  mid    = studbook$Dam,
  sex    = studbook$sex_ped
) %>% discard(., \(x) pedtools::pedsize(x) <= 1)

The object pedig most likely contains a list of multiple pedigree objects, as any founders or lineages not represented in the current living population will form discrete, disconnected trees. There also will probably be several “singletons” which are founders without any connections to the living population. That is why the code above also included a line to thin out your dataset by ignoring the founders that never reproduced.

Pedigree Plot

Now we will take a look at your data in the form of the traditional pedigree structures we covered in class. The code below will prepare some formatting variables to assign color-coding before plotting the pedigree(s) you already created.

ped_list_plots <- plot_ped_series(pedig, colors, studbook)
ped_list_plots
$`_comp5`

$`_comp11`

$`_comp13`

$`_comp23`

$`_comp26`

Most of you probably still have multiple plots, one of which looks much more complicated than the others. We can thin our dataset out even more by filtering for just those individuals still represented in the living population. That means we want to set aside any of the lineages where the descendants are no longer related to any living individuals through surviving siblings, offspring, nieces, nephews, etc. In most cases, that will leave us with one large and complicated pedigree representing all the contemporary diversity in our population. You should inspect the plots about and decide which one to carry forward for your analysis, based on this concept. Once you have done so, update the "_comp5" in the code chunk below with the title you see for your chosen plot. You will need to click “run code” before proceeding. Note: you can come back and run the code chunk again any time with a different name inserted, and all future steps will update based on that selection.

includes_living <- c("_comp5")
ped.living  <- keep_at(pedig, includes_living)
ped.living  <- ped.living[[1]]
famid(ped.living) <- "Current"
ped_living_plot <- plot_pedigree(ped.living, 
              "Pedigree of Living Population", 
              studbook,
              set_ped_fills(ped.living, colors, studbook))
pedigree-plot

Better Pedigree Graphics

I don’t love the pedtools graphics system and lack of customization/interactivity, so if we want to look more closely at the details of relationships and clustering across a complex pedigree, we can transfer the data produced by pedtools into some other visualization packages. In this case, we will use visNetwork Run the chunk below to render two different visualizations.

ped.net  <- ped_network(ped.living, colors, studbook)
ped.vert <- ped.net %>% visHierarchicalLayout(direction = "UD", shakeTowards = "roots")

Hierarchical Layout

First, you will generate a hierarchical plot that resembles the traditional pedigree structure above. Note that the horizontal-bar icons represent each mother-father pair with one or more shared offspring. The dotted lines lead from each parent to the icon representing their parental unit, with color-coding and labels corresponding to the institution where the pair produced their shared offspring. The solid lines coming from each bar-icon connect the parental unit to the offspring that would be full-siblings.

Note: these plots depend on some more memory-intensive javascript code, so you will experience more of a delay than usual before the image loads. If you see a blank plotting area at first, just wait a few minutes for the image to appear.

Network Layout

Second, you will view the same plot as a network instead of a hierarchy. This gives a clearer representation of the clustering around particularly successful mated pairs and institutions.

Note: this one takes even longer to load than the previous plot.

Generate Detailed Pedigree Statistics

Next, we will generate some of the descriptive statistics that we can use to assess the long-term viability of our populations and make some breeding recommendations.

Founders Represented

We will need information about founders still represented in the living population, which we can summarize with the code below.

founder.summary <- founder_summary(pedig, ped.living, studbook)
founder.vis     <- founder.summary %>% studbook_react(., cols = founder.cols(.))

Contemporary Population

Now we will generate some additional stats to give us a similar glimpse of the historical lineage, current representation, and breeding success of our living population.

Inbreeding Coefficients

We will use the pedigree object to calculate an inbreeding coefficient for each individual in the living population.

Recall the definition of inbreeding coefficients…<>

The autosomal inbreeding coefficient of a pedigree member is defined as the probability that, at a random autosomal locus, the two alleles carried by the member are identical by descent, relative to the pedigree.

It follows from the definition that the inbreeding coefficient of a non-founder equals the kinship coefficient of the parents.

<>

inbred.ped <- inbreeding(ped.living) %>% enframe(name = "ID", value = "inbred")
inbred.tbl <- datatable(inbred.ped)

Kinship Coefficients

This gives us some information about each individual’s genetic history, especially their potential to contribute more/less genetic diversity to the next generation, but what does that mean when they are matched to an individual with a higher/lower inbreeding coefficient? When we match individuals, two individuals with moderate inbreeding coefficients but little shared ancestory might still have a positive or neutral impact on the population’s long-term viability. That is why we will also calculate a pairwise metric to score each potential match - the kinship coefficient.

Recall the definition of kinship coefficients…<>

For two (possibly equal) members A, B of a pedigree, their autosomal (resp. X-chromosomal) kinship coefficient is defined as the probability that a random allele from A and a random allele from B, sampled at the same autosomal (resp. X-chromosomal) locus, are identical by descent, relative to the pedigree.

<>

kinship.ped    <- ribd::kinship(ped.living)
living.ped     <- intersect(living, rownames(kinship.ped))
living.kinship <- kinship.ped[living.ped, living.ped]
kinship.mat    <- datatable(living.kinship)

The code above created a matrix that we will use in other code chunks below.

Other Summary Stats

We can also extract some basic counts from our pedigree for each individual before we create some visual summaries below.

living.summary <- family_history(ped.living, studbook) %>% 
  studbook_react(., 
                 cols         = living.cols(.), 
                 columnGroups = list(kin.group())
                 )

Overall Relatedness and Kinship

Now we will use some of the demographic stats we already computed to begin modeling our expected patterns in relatedness and inbreeding across the living population. First, we need to create a matrix representing every living pair’s kinship coefficient. We will return to these pairwise relationships soon, but for now we will use it to compute our population-level statistics.

Visualize Summary Table

Now you can view a summary of the statistics we just calculated and compare them to the summaries provided in the Pygmy Loris BTP. You will refer back to this table and the figures compiled so far to write your own version of a summary (1-2 paragraphs max).

kinship.summary <- kinship_summary(pedig, ped.living, studbook) %>%
  mutate(across(where(is.numeric), ~round(., digits = 3))) %>%
  reactable(theme = minty(font_size = 17), columns = kin.cols())

Pairwise Summary Stats

coeff_living <- coeffTable(ped.living, coeff = c("f", "phi", "deg", "kappa")) %>%
  filter(id1 %in% living(studbook) & id2 %in% living(studbook)) %>%
  mutate(across(where(is.numeric), ~ round(., digits = 3))) %>%
  select(id1, 
         id2, 
         degree = deg, 
         inbred = f2, 
         phi, 
         kappa0 = k0, 
         kappa1 = k1, 
         kappa2 = k2)
example <- basicRelationships %>%
  mutate(across(where(is.numeric), ~ round(., digits = 3))) %>%
  select(label, relationship, phi, starts_with("kappa")) %>% datatable()
coeff.tbl <- datatable(coeff_living)

Kinship Coefficients

It is much easier to visualize pairwise relationships as matrices or multidimensional plots. Let’s create some graphical visualizations to interpret these stats. The code below will produce a basic visualization of the overall patterns in relatedness between all living individuals in the current population. Each cell will be colored by the pairwise kinship coefficients we computed above.

kin.plot <- matrix.heatmap(living.kinship, 
                           "Kinship Across Living Population", 
                           "Kinship Coefficients")

Matched Pairs

Finally, we want to look closer at potential breeding pairs to do some match-making, so let’s generate some more pedigree statistics per individual and then visualize a plot of breeding pairs.

Kinship Coefficients

kinship_btp  <- subset_matrix_living(living.kinship, studbook)
btp.plot     <- matrix.heatmap(kinship_btp, "Kinship Values for Potential Mate Pairs", "Kinship Vals")

Select & Visualize Match

Once you choose some potential pairs, you should plug their ID’s into the code chunks below. You need to click “run code” to log your entry for selected pair ids before proceeding. This will give you a few more graphics to inspect their relationship. You can easily swap out different IDs and re-run the code chunks to see those revised results until you find the pairs you are happy with.

male   <- c("2652") # Replace the number here with the ID number of your male of interest.
female <- c("2677") # Replace the number here with the ID number of your female of interest.
pair   <- c(male, female)

IBD and Kappa Coefficients

Now we are going to print something called a Kappa Triangle. This is a way to visualize the most probable identity by descent (IBD) for this pair in a 2-dimensional space.

kappa.pair <- coeff_living %>%
  filter(xor((id1 == male   & id2 == female), 
             (id1 == female & id2 == male))) %>%
  select(id1, id2, kappa0, kappa1, kappa2)
kappa.tbl  <- datatable(kappa.pair)
kappa.tri <- showInTriangle(kappa.pair, plotType = "plotly")

The red X mark represents the relationship between your hypothetical pair, while the letters in the graphic above are reference points for where different relationships would fall in that space. The summary table below contains a key for those abbreviations (and their associated values plotted above).

Annotated Subplots for Match

Now we can reconstruct our network visualizations with clearer annotations to focus on our proposed pair.

Recall that these plots can be slower to load, but they should be quicker now that we are only working with a subplot.

Also, note that the subtitle of these plots now contains a printed summary produced by the verbalise package that is part of pedsuite.

pair.net  <- subset_network_btp(ped.living, colors, pair, studbook)
pair.vert <- pair.net %>% visHierarchicalLayout(direction = "UD", shakeTowards = "roots")

Make your Plan and Report

Now you should use all the information provided to match at least one breeding pair for your new exhibit and add a paragraph in which you explain this choice and plan.

Demography

Examples

The Prosimian TAG has set a target population size of 55 animals in the Pygmy Slow Loris SSP population. The managed population has been increasing (λ = 0.96) historically, and has retained 92.39% of its founding gene diversity.

This SSP species first appeared in AZA facilities in 1968 when a single male was confiscated and transferred to the Honolulu Zoo. From 1968 – 1986, the population size remained low, never exceeding four individuals, and the Honolulu Zoo remained the only holding institution. The current SSP population was founded in 1987 when the San Diego Zoo, Duke Lemur Center, and Cincinnati Zoo imported 29 individuals from Sweden. The first recorded births occurred in 1988 at all three facilities that worked to import animals. The population steadily grew to a peak of 76 individuals by 2011 (Figure 1). This growth can largely be attributed to successful breeding and secondly to a small number of continued imports. Since 2012, the population has experienced a notable decline in size primarily due to insufficient reproduction. The reasons for this low, inconsistent reproduction in recent years are currently unclear, but may be associated with husbandry, particularly diet. However, the population has shown growth over the last five years again and there are enough births to offset deaths in the population.

Results

Summary

Add your text here.

Genetics

Examples

Genetic values are calculated after incorporating pedigree assumptions and removing excluded individuals. Analysis of the studbook indicates that this SSP is descended from 30 founders with no potential founders remaining (Figure 3, Table 2). The gene diversity of the population is 92%. Based on current founder representations, gene diversity is equivalent to that found in approximately six founders. The current mean kinship in the population is 0.0799; first-cousins have a kinship of 0.0625, which means that the average relationship in the population is slightly closer than that of noninbred first-cousins.

Population management theory suggests genetic management should strive to maintain thresholds for tolerance of gene diversity loss. The standard goal is 90% gene diversity retention for 100 years. Decreases in gene diversity below 90% of that in the founding population have been associated with increasingly compromised reproduction by, among other factors, lower birth/hatch weights, smaller litter/clutch sizes, and greater neonatal mortality in some species.

Based on current population parameters, gene diversity is projected to decline to 66% over the next 100 years if the current population grows to the recommended target size of 55 (Table 3, Scenario a). The most effective ways this population can maintain more gene diversity are to have an increasing growth rate (vs. stable) and a larger long-term population size. The effective population size is high and is helping to maintain gene diversity in this population.

Results

pedigree-plot

Summary

Add your text here.

Recommendations

Examples

NY BRONX
Bronx Zoo/Wildlife Conservation Society
SB ID Local ID Sex Age Disposition Location Breeding With
2623 M14052 M 14 HOLD NY BRONX BREED WITH 2640
2640 M14053 F 12 HOLD NY BRONX BREED WITH 2623
2682 M19007 M 7 RECEIVE FROM BLOOMINGT BREED WITH 2708
2708 116283 F 4 RECEIVE FROM NZP-WASH BREED WITH 2682
NZP-WASH
Smithsonian National Zoological Park
SB ID Local ID Sex Age Disposition Location Breeding With
2708 116283 4 14 SEND TO NY BRONX BREED WITH 2682
2715 116282 3 12 HOLD NZP-WASH DO NOT BREED
2735 116461 0 7 HOLD NZP-WASH DO NOT BREED
2736 116462 0 4 HOLD NZP-WASH DO NOT BREED
OMAHA
Omaha’s Henry Doorly Zoo
SB ID Local ID Sex Age Disposition Location Breeding With
2652 24485 M 11 HOLD OMAHA BREED WITH 2677
2677 24709 F 7 HOLD OMAHA BREED WITH 2652

Results

Summary

Note: For your summary you should write a few sentences explaining the choice you made. You may use a table format like this, a bulleted list, or a paragraph to present your proposed breeding pair(s).

Add your text here.

LS0tCnRpdGxlOiAiUG9wdWxhdGlvbiBEYXRhIE1hbmFnZW1lbnQgTGFiIgp0dXRvcmlhbDoKICBpZDogInBvcGRhdGEiCiAgdmVyc2lvbjogMS4wCmF1dGhvcjogIkRyLiBSaWNoIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdGhlbWU6CiAgICAgIGJvb3Rzd2F0Y2g6IGZsYXRseQogICAgY29kZV9mb2xkaW5nOiAic2hvdyIKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCgpgYGB7ciBzZXR1cCwgY29udGV4dD0ic2V0dXAiLCBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KHpvb2xhYnMpCmxpYnJhcnkoYnNsaWIpCmxpYnJhcnkoZG93bmxvYWR0aGlzKQpsaWJyYXJ5KERUKQpsaWJyYXJ5KGh0bWx0b29scykKbGlicmFyeShodG1sd2lkZ2V0cykKbGlicmFyeShrbml0cikKbGlicmFyeShwZWRzdWl0ZSkKbGlicmFyeShyZWFjdGFibGUpCmxpYnJhcnkocmVhY3RhYmxlZm10cikKbGlicmFyeShzaGlueSkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkodmlzTmV0d29yaykKa25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGNvbGxhcHNlID0gVFJVRSwKICBtZXNzYWdlID0gRkFMU0UsCiAgd2FybmluZyA9IEZBTFNFLAogIGVjaG8gICAgPSBUUlVFLAogIGluY2x1ZGUgPSBUUlVFLAogIGV2YWwgICAgPSBUUlVFLAogIGNvbW1lbnQgPSAiIgopCmNvbG9ycyAgIDwtICAgbGlzdCgKICAgIGYgICAgPSAiI0Q1MzI4OEZGIiwKICAgIG0gICAgPSAiIzNGNDU5QkZGIiwKICAgIHUgICAgPSAiIzIxQjE0QkZGIiwKICAgIHNpcmUgPSAiIzNGNDU5QjMzIiwKICAgIGRhbSAgPSAiI0Q1MzI4ODMzIiwKICAgIGVtcGggPSAiI0RDODA0NUZGIiwKICAgIHNlcSAgPSAicmNhcnRvY29sb3I6OlN1bnNldCIsCiAgICBkaXYgID0gInJjYXJ0b2NvbG9yOjpUZW1wcyIsCiAgICByYW5kID0gImtocm9tYTo6c3RyYXRpZ3JhcGh5IgogICkKY29sLnBhbCAgPC0gc2V0X3Bsb3RseV9wYWwoY29sb3JzKQpmaWxsLnBhbCA8LSBsaWdodGVuX3BhbGV0dGUoY29sLnBhbCwgMjYpCgoKc3R1ZGJvb2sgPC0gbG9hZF9zdHVkYm9vaygpCnRpbWVsaW5lIDwtIGxvYWRfdGltZWxpbmUoKQpsaXZpbmcgICA8LSBsaXZpbmcoc3R1ZGJvb2spCmRlY2Vhc2VkIDwtIGRlY2Vhc2VkKHN0dWRib29rKQpgYGAKCiMgU3R1ZGJvb2sgT3ZlcnZpZXcKCkxldCdzIGNyZWF0ZSBhIGdyYXBoaWNhbCB0YWJsZSB0byBzdW1tYXJpemUgb3VyIHN0dWRib29rIGRhdGEuCgpgYGB7ciBzdHVkYm9vay12aWV3LCBleGVyY2lzZT1UUlVFfQpzdHVkYm9vay50YmwgPC0gc3R1ZGJvb2sgJT4lIHN0dWRib29rX3JlYWN0KC4sIHN0dWRib29rLmNvbHMoLiksIGdyb3VwQnkgPSAiU3RhdHVzIikKc3R1ZGJvb2sudGJsCmBgYAoKIyMgU3VtbWFyaXplIERlbW9ncmFwaGljIFRyZW5kcwoKTGV0J3MgbG9vayBhdCB0aGUgYmFzaWMgcG9wdWxhdGlvbiBzaXplIG92ZXIgdGltZSB0byBiZWdpbiBxdWFudGlmeWluZyBncm93dGggcmF0ZSBhbmQgYXNzZXNzaW5nIG92ZXJhbGwgcG9wdWxhdGlvbiB2aWFiaWxpdHkuCgpgYGB7ciBjZW5zdXMtc2V0dXAsIGV4ZXJjaXNlPVRSVUV9CnJhdy5jb3VudHMgICAgICA8LSBjZW5zdXModGltZWxpbmUsIHN0dWRib29rLCAieWVhcnMiKQpgYGAKCmBgYHtyIGNlbnN1cywgZXhlcmNpc2U9VFJVRX0KcG9wdWxhdGlvbi5wbG90IDwtIHBsb3RfY2Vuc3VzKHJhdy5jb3VudHMsIGNvbG9ycywgIlB5Z215IExvcmlzIFBvcHVsYXRpb24gVHJlbmRzIDE5NjYtMjAyNSIpCnBvcHVsYXRpb24ucGxvdApgYGAKCiMjIExpZmUgVGFibGUgQ2FsY3VsYXRpb25zIAoKTm93IHdlIHdpbGwgdXNlIGFubnVhbCBiaXJ0aCBjb3VudHMgdG8gZm9sbG93IHRoZSBzdGFuZGFyZCBtZXRob2QgZm9yIGNvbXB1dGluZyBhIG5vcm1hbGl6ZWQgcmF0ZSBvZiBncm93dGggb3ZlciB0aGUgcGFzdCA1IHllYXJzIGNvbXBhcmVkIHRvIGFsbCB5ZWFycy4KCmBgYHtyIGxpZmV0YWIsIGV4ZXJjaXNlPVRSVUV9CmxpZmVUYWIgICAgICA8LSBjb2hvcnRfbGlmZVRhYih0aW1lbGluZSwgc3R1ZGJvb2ssIDE5ODUsIDIwMjQsIDUsIDIyKQpgYGAKCkFuZCB0aGVuIHdlIHdpbGwgcGxvdCB0cmVuZHMgaW4gdGhpcyBncm93dGggcmF0ZSBiYXNlZCBvbiBiaXJ0aCB5ZWFyLgoKYGBge3IgbGFtYmRhLCBleGVyY2lzZT1UUlVFLCBleGVyY2lzZS5zZXR1cD0ibGlmZXRhYiJ9CmxhbWJkYS5wbG90ICA8LSBwbG90X2xhbWJkYShsaWZlVGFiLCBjb2xvcnMsICJQb3B1bGF0aW9uIEdyb3d0aCBSYXRlcyBieSBDb2hvcnQiKQpsYW1iZGEucGxvdApgYGAKCiMjIENvbnN0cnVjdCBQZWRpZ3JlZSAKCk5vdyB3ZSB3aWxsIHVzZSBhIHBhY2thZ2UgY2FsbGVkIGBwZWR0b29sc2AgdG8gY3JlYXRlIG91ciBwZWRpZ3JlZSBvYmplY3RzIGJhc2VkIG9uIHRoZSBzdHVkYm9vayBkYXRhLiBXZSB3aWxsIHVzZSB0aGUgcGVkaWdyZWUgb2JqZWN0IHRvIGdlbmVyYXRlIGFsbCBnZW5ldGljIHN0YXRzIGFuZCBwcm9qZWN0aW9ucy4KCmBgYHtyIHBlZGlncmVlLCBleGVyY2lzZT1UUlVFfQpwZWRpZyA8LSBwZWQoCiAgaWQgICAgID0gc3R1ZGJvb2skSUQsCiAgZmlkICAgID0gc3R1ZGJvb2skU2lyZSwKICBtaWQgICAgPSBzdHVkYm9vayREYW0sCiAgc2V4ICAgID0gc3R1ZGJvb2skc2V4X3BlZAopICU+JSBkaXNjYXJkKC4sIFwoeCkgcGVkdG9vbHM6OnBlZHNpemUoeCkgPD0gMSkKYGBgCgpUaGUgb2JqZWN0IGBwZWRpZ2AgbW9zdCBsaWtlbHkgY29udGFpbnMgYSBsaXN0IG9mIG11bHRpcGxlIHBlZGlncmVlIG9iamVjdHMsIGFzIGFueSBmb3VuZGVycyBvciBsaW5lYWdlcyBub3QgcmVwcmVzZW50ZWQgaW4gdGhlIGN1cnJlbnQgbGl2aW5nIHBvcHVsYXRpb24gd2lsbCBmb3JtIGRpc2NyZXRlLCBkaXNjb25uZWN0ZWQgdHJlZXMuIFRoZXJlIGFsc28gd2lsbCBwcm9iYWJseSBiZSBzZXZlcmFsICJzaW5nbGV0b25zIiB3aGljaCBhcmUgZm91bmRlcnMgd2l0aG91dCBhbnkgY29ubmVjdGlvbnMgdG8gdGhlIGxpdmluZyBwb3B1bGF0aW9uLiBUaGF0IGlzIHdoeSB0aGUgY29kZSBhYm92ZSBhbHNvIGluY2x1ZGVkIGEgbGluZSB0byB0aGluIG91dCB5b3VyIGRhdGFzZXQgYnkgaWdub3JpbmcgdGhlIGZvdW5kZXJzIHRoYXQgbmV2ZXIgcmVwcm9kdWNlZC4KCiMjIyBQZWRpZ3JlZSBQbG90CgpOb3cgd2Ugd2lsbCB0YWtlIGEgbG9vayBhdCB5b3VyIGRhdGEgaW4gdGhlIGZvcm0gb2YgdGhlIHRyYWRpdGlvbmFsIHBlZGlncmVlIHN0cnVjdHVyZXMgd2UgY292ZXJlZCBpbiBjbGFzcy4gVGhlIGNvZGUgYmVsb3cgd2lsbCBwcmVwYXJlIHNvbWUgZm9ybWF0dGluZyB2YXJpYWJsZXMgdG8gYXNzaWduIGNvbG9yLWNvZGluZyBiZWZvcmUgcGxvdHRpbmcgdGhlIHBlZGlncmVlKHMpIHlvdSBhbHJlYWR5IGNyZWF0ZWQuCgpgYGB7ciBwZWRwbG90LWxpc3QsIGV4ZXJjaXNlPVRSVUUsIGV4ZXJjaXNlLnNldHVwPSJwZWRpZ3JlZSJ9CnBlZF9saXN0X3Bsb3RzIDwtIHBsb3RfcGVkX3NlcmllcyhwZWRpZywgY29sb3JzLCBzdHVkYm9vaykKcGVkX2xpc3RfcGxvdHMKYGBgCgpNb3N0IG9mIHlvdSBwcm9iYWJseSBzdGlsbCBoYXZlIG11bHRpcGxlIHBsb3RzLCBvbmUgb2Ygd2hpY2ggbG9va3MgbXVjaCBtb3JlIGNvbXBsaWNhdGVkIHRoYW4gdGhlIG90aGVycy4gV2UgY2FuIHRoaW4gb3VyIGRhdGFzZXQgb3V0IGV2ZW4gbW9yZSBieSBmaWx0ZXJpbmcgZm9yIGp1c3QgdGhvc2UgaW5kaXZpZHVhbHMgc3RpbGwgcmVwcmVzZW50ZWQgaW4gdGhlIGxpdmluZyBwb3B1bGF0aW9uLiBUaGF0IG1lYW5zIHdlIHdhbnQgdG8gc2V0IGFzaWRlIGFueSBvZiB0aGUgbGluZWFnZXMgd2hlcmUgdGhlIGRlc2NlbmRhbnRzIGFyZSBubyBsb25nZXIgcmVsYXRlZCB0byBhbnkgbGl2aW5nIGluZGl2aWR1YWxzIHRocm91Z2ggc3Vydml2aW5nIHNpYmxpbmdzLCBvZmZzcHJpbmcsIG5pZWNlcywgbmVwaGV3cywgZXRjLiBJbiBtb3N0IGNhc2VzLCB0aGF0IHdpbGwgbGVhdmUgdXMgd2l0aCBvbmUgbGFyZ2UgYW5kIGNvbXBsaWNhdGVkIHBlZGlncmVlIHJlcHJlc2VudGluZyBhbGwgdGhlIGNvbnRlbXBvcmFyeSBkaXZlcnNpdHkgaW4gb3VyIHBvcHVsYXRpb24uXG4KXG4KWW91IHNob3VsZCBpbnNwZWN0IHRoZSBwbG90cyBhYm91dCBhbmQgZGVjaWRlIHdoaWNoIG9uZSB0byBjYXJyeSBmb3J3YXJkIGZvciB5b3VyIGFuYWx5c2lzLCBiYXNlZCBvbiB0aGlzIGNvbmNlcHQuIE9uY2UgeW91IGhhdmUgZG9uZSBzbywgdXBkYXRlIHRoZSBgIl9jb21wNSJgIGluIHRoZSBjb2RlIGNodW5rIGJlbG93IHdpdGggdGhlIHRpdGxlIHlvdSBzZWUgZm9yIHlvdXIgY2hvc2VuIHBsb3QuIFlvdSB3aWxsIG5lZWQgdG8gY2xpY2sgInJ1biBjb2RlIiBiZWZvcmUgcHJvY2VlZGluZy5cbgpcbgoqTm90ZTogeW91IGNhbiBjb21lIGJhY2sgYW5kIHJ1biB0aGUgY29kZSBjaHVuayBhZ2FpbiBhbnkgdGltZSB3aXRoIGEgZGlmZmVyZW50IG5hbWUgaW5zZXJ0ZWQsIGFuZCBhbGwgZnV0dXJlIHN0ZXBzIHdpbGwgdXBkYXRlIGJhc2VkIG9uIHRoYXQgc2VsZWN0aW9uLioKCmBgYHtyIHNldHVwLXBlZC1saXZpbmd9CmluY2x1ZGVzX2xpdmluZyA8LSBjKCJfY29tcDUiKQpgYGAKCmBgYHtyIHBlZC1saXZpbmd9CnBlZC5saXZpbmcgIDwtIGtlZXBfYXQocGVkaWcsIGluY2x1ZGVzX2xpdmluZykKcGVkLmxpdmluZyAgPC0gcGVkLmxpdmluZ1tbMV1dCmZhbWlkKHBlZC5saXZpbmcpIDwtICJDdXJyZW50IgpwZWRfbGl2aW5nX3Bsb3QgPC0gcGxvdF9wZWRpZ3JlZShwZWQubGl2aW5nLCAKICAgICAgICAgICAgICAiUGVkaWdyZWUgb2YgTGl2aW5nIFBvcHVsYXRpb24iLCAKICAgICAgICAgICAgICBzdHVkYm9vaywKICAgICAgICAgICAgICBzZXRfcGVkX2ZpbGxzKHBlZC5saXZpbmcsIGNvbG9ycywgc3R1ZGJvb2spKQpgYGAKCgpgYGB7ciBwbG90LXBlZC1saXZpbmcsICBlY2hvPUZBTFNFfQphY2NvcmRpb24ob3BlbiA9IEZBTFNFLCBjbGFzcyA9ICJicy1zZWNvbmRhcnkiLCBhY2NvcmRpb25fcGFuZWwoIkNsaWNrIHRvIHNlZSBwZWRpZ3JlZSIsIHBlZF9saXZpbmdfcGxvdCkpCmBgYAoKYGBge3IgZG93bmxvYWQtcGVkLWxpdmluZywgZWNobz1GQUxTRX0KZmlsZSA8LSBjYXB0dXJlX3BlZF9maWxlKHBlZC5saXZpbmcsIAogICAgICAgICAgICAgICJQZWRpZ3JlZSBvZiBMaXZpbmcgUG9wdWxhdGlvbiIsIAogICAgICAgICAgICAgIHN0dWRib29rLAogICAgICAgICAgICAgIHNldF9wZWRfZmlsbHMocGVkLmxpdmluZywgY29sb3JzLCBzdHVkYm9vaykpCmRvd25sb2FkX2ZpbGUoCiAgcGF0aCAgICAgICA9IGZpbGUsCiAgb3V0cHV0X25hbWUgID0gInBlZGlncmVlX3Bsb3QiLAogIGJ1dHRvbl9sYWJlbCA9ICJDbGljayB0byBkb3dubG9hZCBwZWRpZ3JlZSIsCiAgYnV0dG9uX3R5cGUgID0gInN1Y2Nlc3MiCikKYGBgCgoKIyMjIEJldHRlciBQZWRpZ3JlZSBHcmFwaGljcyAKCkkgZG9uJ3QgbG92ZSB0aGUgcGVkdG9vbHMgZ3JhcGhpY3Mgc3lzdGVtIGFuZCBsYWNrIG9mIGN1c3RvbWl6YXRpb24vaW50ZXJhY3Rpdml0eSwgc28gaWYgd2Ugd2FudCB0byBsb29rIG1vcmUgY2xvc2VseSBhdCB0aGUgZGV0YWlscyBvZiByZWxhdGlvbnNoaXBzIGFuZCBjbHVzdGVyaW5nIGFjcm9zcyBhIGNvbXBsZXggcGVkaWdyZWUsIHdlIGNhbiB0cmFuc2ZlciB0aGUgZGF0YSBwcm9kdWNlZCBieSBwZWR0b29scyBpbnRvIHNvbWUgb3RoZXIgdmlzdWFsaXphdGlvbiBwYWNrYWdlcy4gSW4gdGhpcyBjYXNlLCB3ZSB3aWxsIHVzZSBgdmlzTmV0d29ya2AgUnVuIHRoZSBjaHVuayBiZWxvdyB0byByZW5kZXIgdHdvIGRpZmZlcmVudCB2aXN1YWxpemF0aW9ucy4KCmBgYHtyIHNldHVwLXBlZG5ldH0KcGVkLm5ldCAgPC0gcGVkX25ldHdvcmsocGVkLmxpdmluZywgY29sb3JzLCBzdHVkYm9vaykKcGVkLnZlcnQgPC0gcGVkLm5ldCAlPiUgdmlzSGllcmFyY2hpY2FsTGF5b3V0KGRpcmVjdGlvbiA9ICJVRCIsIHNoYWtlVG93YXJkcyA9ICJyb290cyIpCmBgYAoKIyMjIyBIaWVyYXJjaGljYWwgTGF5b3V0CgpGaXJzdCwgeW91IHdpbGwgZ2VuZXJhdGUgYSBoaWVyYXJjaGljYWwgcGxvdCB0aGF0IHJlc2VtYmxlcyB0aGUgdHJhZGl0aW9uYWwgcGVkaWdyZWUgc3RydWN0dXJlIGFib3ZlLiBOb3RlIHRoYXQgdGhlIGhvcml6b250YWwtYmFyIGljb25zIHJlcHJlc2VudCBlYWNoIG1vdGhlci1mYXRoZXIgcGFpciB3aXRoIG9uZSBvciBtb3JlIHNoYXJlZCBvZmZzcHJpbmcuIFRoZSBkb3R0ZWQgbGluZXMgbGVhZCBmcm9tIGVhY2ggcGFyZW50IHRvIHRoZSBpY29uIHJlcHJlc2VudGluZyB0aGVpciBwYXJlbnRhbCB1bml0LCB3aXRoIGNvbG9yLWNvZGluZyBhbmQgbGFiZWxzIGNvcnJlc3BvbmRpbmcgdG8gdGhlIGluc3RpdHV0aW9uIHdoZXJlIHRoZSBwYWlyIHByb2R1Y2VkIHRoZWlyIHNoYXJlZCBvZmZzcHJpbmcuIFRoZSBzb2xpZCBsaW5lcyBjb21pbmcgZnJvbSBlYWNoIGJhci1pY29uIGNvbm5lY3QgdGhlIHBhcmVudGFsIHVuaXQgdG8gdGhlIG9mZnNwcmluZyB0aGF0IHdvdWxkIGJlIGZ1bGwtc2libGluZ3MuCgo+Tm90ZTogdGhlc2UgcGxvdHMgZGVwZW5kIG9uIHNvbWUgbW9yZSBtZW1vcnktaW50ZW5zaXZlIGphdmFzY3JpcHQgY29kZSwgc28geW91IHdpbGwgZXhwZXJpZW5jZSBtb3JlIG9mIGEgZGVsYXkgdGhhbiB1c3VhbCBiZWZvcmUgdGhlIGltYWdlIGxvYWRzLiBJZiB5b3Ugc2VlIGEgYmxhbmsgcGxvdHRpbmcgYXJlYSBhdCBmaXJzdCwganVzdCB3YWl0IGEgZmV3IG1pbnV0ZXMgZm9yIHRoZSBpbWFnZSB0byBhcHBlYXIuCgoKYGBge3IgcGxvdC1wZWR2ZXJ0LCAgZWNobz1GQUxTRX0KYWNjb3JkaW9uKG9wZW4gPSBGQUxTRSwgY2xhc3MgPSAiYnMtc2Vjb25kYXJ5IiwgYWNjb3JkaW9uX3BhbmVsKCJDbGljayB0byBzZWUgb3V0cHV0IiwgcGVkLnZlcnQpKQpgYGAKCiMjIyMgTmV0d29yayBMYXlvdXQKClNlY29uZCwgeW91IHdpbGwgdmlldyB0aGUgc2FtZSBwbG90IGFzIGEgbmV0d29yayBpbnN0ZWFkIG9mIGEgaGllcmFyY2h5LiBUaGlzIGdpdmVzIGEgY2xlYXJlciByZXByZXNlbnRhdGlvbiBvZiB0aGUgY2x1c3RlcmluZyBhcm91bmQgcGFydGljdWxhcmx5IHN1Y2Nlc3NmdWwgbWF0ZWQgcGFpcnMgYW5kIGluc3RpdHV0aW9ucy4KCj5Ob3RlOiB0aGlzIG9uZSB0YWtlcyBldmVuIGxvbmdlciB0byBsb2FkIHRoYW4gdGhlIHByZXZpb3VzIHBsb3QuCgpgYGB7ciBwbG90LXBlZG5ldCwgIGVjaG89RkFMU0V9CmFjY29yZGlvbihvcGVuID0gRkFMU0UsIGNsYXNzID0gImJzLXNlY29uZGFyeSIsIGFjY29yZGlvbl9wYW5lbCgiQ2xpY2sgdG8gc2VlIG91dHB1dCIsIHBlZC5uZXQpKQpgYGAKCiMjIEdlbmVyYXRlIERldGFpbGVkIFBlZGlncmVlIFN0YXRpc3RpY3MgCgpOZXh0LCB3ZSB3aWxsIGdlbmVyYXRlIHNvbWUgb2YgdGhlIGRlc2NyaXB0aXZlIHN0YXRpc3RpY3MgdGhhdCB3ZSBjYW4gdXNlIHRvIGFzc2VzcyB0aGUgbG9uZy10ZXJtIHZpYWJpbGl0eSBvZiBvdXIgcG9wdWxhdGlvbnMgYW5kIG1ha2Ugc29tZSBicmVlZGluZyByZWNvbW1lbmRhdGlvbnMuCgojIyMgRm91bmRlcnMgUmVwcmVzZW50ZWQKCldlIHdpbGwgbmVlZCBpbmZvcm1hdGlvbiBhYm91dCBmb3VuZGVycyBzdGlsbCByZXByZXNlbnRlZCBpbiB0aGUgbGl2aW5nIHBvcHVsYXRpb24sIHdoaWNoIHdlIGNhbiBzdW1tYXJpemUgd2l0aCB0aGUgY29kZSBiZWxvdy4KCmBgYHtyIGZvdW5kZXJzLXNldHVwfQpmb3VuZGVyLnN1bW1hcnkgPC0gZm91bmRlcl9zdW1tYXJ5KHBlZGlnLCBwZWQubGl2aW5nLCBzdHVkYm9vaykKZm91bmRlci52aXMgICAgIDwtIGZvdW5kZXIuc3VtbWFyeSAlPiUgc3R1ZGJvb2tfcmVhY3QoLiwgY29scyA9IGZvdW5kZXIuY29scyguKSkKYGBgCgpgYGB7ciBmb3VuZGVycywgIGVjaG89RkFMU0V9CmFjY29yZGlvbihvcGVuID0gRkFMU0UsIGNsYXNzID0gImJzLXNlY29uZGFyeSIsIGFjY29yZGlvbl9wYW5lbCgiQ2xpY2sgdG8gc2VlIG91dHB1dCIsIGZvdW5kZXIudmlzKSkKYGBgCgojIyMgQ29udGVtcG9yYXJ5IFBvcHVsYXRpb24gCgpOb3cgd2Ugd2lsbCBnZW5lcmF0ZSBzb21lIGFkZGl0aW9uYWwgc3RhdHMgdG8gZ2l2ZSB1cyBhIHNpbWlsYXIgZ2xpbXBzZSBvZiB0aGUgaGlzdG9yaWNhbCBsaW5lYWdlLCBjdXJyZW50IHJlcHJlc2VudGF0aW9uLCBhbmQgYnJlZWRpbmcgc3VjY2VzcyBvZiBvdXIgbGl2aW5nIHBvcHVsYXRpb24uCgojIyMjIEluYnJlZWRpbmcgQ29lZmZpY2llbnRzCgpXZSB3aWxsIHVzZSB0aGUgcGVkaWdyZWUgb2JqZWN0IHRvIGNhbGN1bGF0ZSBhbiAqKmluYnJlZWRpbmcgY29lZmZpY2llbnQqKiBmb3IgZWFjaCBpbmRpdmlkdWFsIGluIHRoZSBsaXZpbmcgcG9wdWxhdGlvbi4KCjxkZXRhaWxzPgo8c3VtbWFyeT5SZWNhbGwgdGhlIGRlZmluaXRpb24gb2YgaW5icmVlZGluZyBjb2VmZmljaWVudHMuLi48XHN1bW1hcnk+CjxwPlRoZSBhdXRvc29tYWwgaW5icmVlZGluZyBjb2VmZmljaWVudCBvZiBhIHBlZGlncmVlIG1lbWJlciBpcyBkZWZpbmVkIGFzIHRoZSBwcm9iYWJpbGl0eSB0aGF0LCBhdCBhIHJhbmRvbSBhdXRvc29tYWwgbG9jdXMsIHRoZSAqKnR3byBhbGxlbGVzIGNhcnJpZWQgYnkgdGhlIG1lbWJlciBhcmUgaWRlbnRpY2FsIGJ5IGRlc2NlbnQqKiwgcmVsYXRpdmUgdG8gdGhlIHBlZGlncmVlLjwvcD4KPHA+Kkl0IGZvbGxvd3MgZnJvbSB0aGUgZGVmaW5pdGlvbiB0aGF0IHRoZSBpbmJyZWVkaW5nIGNvZWZmaWNpZW50IG9mIGEgbm9uLWZvdW5kZXIgZXF1YWxzIHRoZSBraW5zaGlwIGNvZWZmaWNpZW50IG9mIHRoZSBwYXJlbnRzLio8L3A+CjxcZGV0YWlscz4KCmBgYHtyIHNldHVwLWluYnJlZWRpbmd9CmluYnJlZC5wZWQgPC0gaW5icmVlZGluZyhwZWQubGl2aW5nKSAlPiUgZW5mcmFtZShuYW1lID0gIklEIiwgdmFsdWUgPSAiaW5icmVkIikKaW5icmVkLnRibCA8LSBkYXRhdGFibGUoaW5icmVkLnBlZCkKYGBgCgpgYGB7ciBpbmJyZWVkaW5nLCBlY2hvPUZBTFNFfQphY2NvcmRpb24ob3BlbiA9IEZBTFNFLCBjbGFzcyA9ICJicy1zZWNvbmRhcnkiLCBhY2NvcmRpb25fcGFuZWwoIkNsaWNrIHRvIHNlZSBvdXRwdXQiLCBpbmJyZWQudGJsKSkKYGBgCgojIyMjIEtpbnNoaXAgQ29lZmZpY2llbnRzCgpUaGlzIGdpdmVzIHVzIHNvbWUgaW5mb3JtYXRpb24gYWJvdXQgZWFjaCBpbmRpdmlkdWFsJ3MgZ2VuZXRpYyBoaXN0b3J5LCBlc3BlY2lhbGx5IHRoZWlyIHBvdGVudGlhbCB0byBjb250cmlidXRlIG1vcmUvbGVzcyBnZW5ldGljIGRpdmVyc2l0eSB0byB0aGUgbmV4dCBnZW5lcmF0aW9uLCBidXQgd2hhdCBkb2VzIHRoYXQgbWVhbiB3aGVuIHRoZXkgYXJlIG1hdGNoZWQgdG8gYW4gaW5kaXZpZHVhbCB3aXRoIGEgaGlnaGVyL2xvd2VyIGluYnJlZWRpbmcgY29lZmZpY2llbnQ/XG4KXG4KV2hlbiB3ZSBtYXRjaCBpbmRpdmlkdWFscywgdHdvIGluZGl2aWR1YWxzIHdpdGggbW9kZXJhdGUgaW5icmVlZGluZyBjb2VmZmljaWVudHMgYnV0IGxpdHRsZSBzaGFyZWQgYW5jZXN0b3J5IG1pZ2h0IHN0aWxsIGhhdmUgYSBwb3NpdGl2ZSBvciBuZXV0cmFsIGltcGFjdCBvbiB0aGUgcG9wdWxhdGlvbidzIGxvbmctdGVybSB2aWFiaWxpdHkuIFRoYXQgaXMgd2h5IHdlIHdpbGwgYWxzbyBjYWxjdWxhdGUgYSBwYWlyd2lzZSBtZXRyaWMgdG8gc2NvcmUgZWFjaCBwb3RlbnRpYWwgbWF0Y2ggLSB0aGUgKipraW5zaGlwIGNvZWZmaWNpZW50KiouCgo8ZGV0YWlscz4KPHN1bW1hcnk+UmVjYWxsIHRoZSBkZWZpbml0aW9uIG9mIGtpbnNoaXAgY29lZmZpY2llbnRzLi4uPFxzdW1tYXJ5Pgo8cD5Gb3IgdHdvIChwb3NzaWJseSBlcXVhbCkgbWVtYmVycyBBLCBCIG9mIGEgcGVkaWdyZWUsIHRoZWlyIGF1dG9zb21hbCAocmVzcC4gWC1jaHJvbW9zb21hbCkga2luc2hpcCBjb2VmZmljaWVudCBpcyBkZWZpbmVkIGFzIHRoZSBwcm9iYWJpbGl0eSB0aGF0IGEgKipyYW5kb20gYWxsZWxlIGZyb20gQSBhbmQgYSByYW5kb20gYWxsZWxlIGZyb20gQiwgc2FtcGxlZCBhdCB0aGUgc2FtZSBhdXRvc29tYWwgKHJlc3AuIFgtY2hyb21vc29tYWwpIGxvY3VzLCBhcmUgaWRlbnRpY2FsIGJ5IGRlc2NlbnQqKiwgcmVsYXRpdmUgdG8gdGhlIHBlZGlncmVlLjwvcD4KPFxkZXRhaWxzPgoKYGBge3Igc2V0dXAta2luc2hpcC1tYXRyaXh9CmtpbnNoaXAucGVkICAgIDwtIHJpYmQ6OmtpbnNoaXAocGVkLmxpdmluZykKbGl2aW5nLnBlZCAgICAgPC0gaW50ZXJzZWN0KGxpdmluZywgcm93bmFtZXMoa2luc2hpcC5wZWQpKQpsaXZpbmcua2luc2hpcCA8LSBraW5zaGlwLnBlZFtsaXZpbmcucGVkLCBsaXZpbmcucGVkXQpraW5zaGlwLm1hdCAgICA8LSBkYXRhdGFibGUobGl2aW5nLmtpbnNoaXApCmBgYAoKVGhlIGNvZGUgYWJvdmUgY3JlYXRlZCBhIG1hdHJpeCB0aGF0IHdlIHdpbGwgdXNlIGluIG90aGVyIGNvZGUgY2h1bmtzIGJlbG93LgoKYGBge3Iga2luc2hpcC1tYXRyaXgsIGVjaG89RkFMU0V9CmFjY29yZGlvbihvcGVuID0gRkFMU0UsIGNsYXNzID0gImJzLXNlY29uZGFyeSIsIGFjY29yZGlvbl9wYW5lbCgiQ2xpY2sgdG8gc2VlIG1hdHJpeCBwcmV2aWV3Iiwga2luc2hpcC5tYXQpKQpgYGAKCiMjIyMgT3RoZXIgU3VtbWFyeSBTdGF0cwoKV2UgY2FuIGFsc28gZXh0cmFjdCBzb21lIGJhc2ljIGNvdW50cyBmcm9tIG91ciBwZWRpZ3JlZSBmb3IgZWFjaCBpbmRpdmlkdWFsIGJlZm9yZSB3ZSBjcmVhdGUgc29tZSB2aXN1YWwgc3VtbWFyaWVzIGJlbG93LgoKYGBge3Igc2V0dXAtbGl2aW5nLXN1bW1hcnl9CmxpdmluZy5zdW1tYXJ5IDwtIGZhbWlseV9oaXN0b3J5KHBlZC5saXZpbmcsIHN0dWRib29rKSAlPiUgCiAgc3R1ZGJvb2tfcmVhY3QoLiwgCiAgICAgICAgICAgICAgICAgY29scyAgICAgICAgID0gbGl2aW5nLmNvbHMoLiksIAogICAgICAgICAgICAgICAgIGNvbHVtbkdyb3VwcyA9IGxpc3Qoa2luLmdyb3VwKCkpCiAgICAgICAgICAgICAgICAgKQpgYGAKCmBgYHtyIGxpdmluZy1zdW1tYXJ5LCAgZWNobz1GQUxTRX0KYWNjb3JkaW9uKG9wZW4gPSBGQUxTRSwgY2xhc3MgPSAiYnMtc2Vjb25kYXJ5IiwgYWNjb3JkaW9uX3BhbmVsKCJDbGljayB0byBzZWUgb3V0cHV0IiwgbGl2aW5nLnN1bW1hcnkpKQpgYGAKCiMjIyBPdmVyYWxsIFJlbGF0ZWRuZXNzIGFuZCBLaW5zaGlwIAoKTm93IHdlIHdpbGwgdXNlIHNvbWUgb2YgdGhlIGRlbW9ncmFwaGljIHN0YXRzIHdlIGFscmVhZHkgY29tcHV0ZWQgdG8gYmVnaW4gbW9kZWxpbmcgb3VyIGV4cGVjdGVkIHBhdHRlcm5zIGluIHJlbGF0ZWRuZXNzIGFuZCBpbmJyZWVkaW5nIGFjcm9zcyB0aGUgbGl2aW5nIHBvcHVsYXRpb24uIEZpcnN0LCB3ZSBuZWVkIHRvIGNyZWF0ZSBhIG1hdHJpeCByZXByZXNlbnRpbmcgZXZlcnkgbGl2aW5nIHBhaXIncyBraW5zaGlwIGNvZWZmaWNpZW50LiBXZSB3aWxsIHJldHVybiB0byB0aGVzZSBwYWlyd2lzZSByZWxhdGlvbnNoaXBzIHNvb24sIGJ1dCBmb3Igbm93IHdlIHdpbGwgdXNlIGl0IHRvIGNvbXB1dGUgb3VyIHBvcHVsYXRpb24tbGV2ZWwgc3RhdGlzdGljcy4KCiMjIyMgVmlzdWFsaXplIFN1bW1hcnkgVGFibGUKCk5vdyB5b3UgY2FuIHZpZXcgYSBzdW1tYXJ5IG9mIHRoZSBzdGF0aXN0aWNzIHdlIGp1c3QgY2FsY3VsYXRlZCBhbmQgY29tcGFyZSB0aGVtIHRvIHRoZSBzdW1tYXJpZXMgcHJvdmlkZWQgaW4gdGhlIFB5Z215IExvcmlzIEJUUC4gWW91IHdpbGwgcmVmZXIgYmFjayB0byB0aGlzIHRhYmxlIGFuZCB0aGUgZmlndXJlcyBjb21waWxlZCBzbyBmYXIgdG8gd3JpdGUgeW91ciBvd24gdmVyc2lvbiBvZiBhIHN1bW1hcnkgKDEtMiBwYXJhZ3JhcGhzIG1heCkuCgpgYGB7ciBzZXR1cC1raW5zaGlwLXN1bW1hcnl9CmtpbnNoaXAuc3VtbWFyeSA8LSBraW5zaGlwX3N1bW1hcnkocGVkaWcsIHBlZC5saXZpbmcsIHN0dWRib29rKSAlPiUKICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpLCB+cm91bmQoLiwgZGlnaXRzID0gMykpKSAlPiUKICByZWFjdGFibGUodGhlbWUgPSBtaW50eShmb250X3NpemUgPSAxNyksIGNvbHVtbnMgPSBraW4uY29scygpKQpgYGAKCmBgYHtyIGtpbnNoaXAtc3VtbWFyeSwgIGVjaG89RkFMU0V9CmFjY29yZGlvbihvcGVuID0gRkFMU0UsIGNsYXNzID0gImJzLXNlY29uZGFyeSIsIGFjY29yZGlvbl9wYW5lbCgiQ2xpY2sgdG8gc2VlIG91dHB1dCIsIGtpbnNoaXAuc3VtbWFyeSkpCmBgYAoKIyMjIyBQYWlyd2lzZSBTdW1tYXJ5IFN0YXRzCgpgYGB7ciBzZXR1cC1jb2VmZi10Ymx9CmNvZWZmX2xpdmluZyA8LSBjb2VmZlRhYmxlKHBlZC5saXZpbmcsIGNvZWZmID0gYygiZiIsICJwaGkiLCAiZGVnIiwgImthcHBhIikpICU+JQogIGZpbHRlcihpZDEgJWluJSBsaXZpbmcoc3R1ZGJvb2spICYgaWQyICVpbiUgbGl2aW5nKHN0dWRib29rKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyh3aGVyZShpcy5udW1lcmljKSwgfiByb3VuZCguLCBkaWdpdHMgPSAzKSkpICU+JQogIHNlbGVjdChpZDEsIAogICAgICAgICBpZDIsIAogICAgICAgICBkZWdyZWUgPSBkZWcsIAogICAgICAgICBpbmJyZWQgPSBmMiwgCiAgICAgICAgIHBoaSwgCiAgICAgICAgIGthcHBhMCA9IGswLCAKICAgICAgICAga2FwcGExID0gazEsIAogICAgICAgICBrYXBwYTIgPSBrMikKZXhhbXBsZSA8LSBiYXNpY1JlbGF0aW9uc2hpcHMgJT4lCiAgbXV0YXRlKGFjcm9zcyh3aGVyZShpcy5udW1lcmljKSwgfiByb3VuZCguLCBkaWdpdHMgPSAzKSkpICU+JQogIHNlbGVjdChsYWJlbCwgcmVsYXRpb25zaGlwLCBwaGksIHN0YXJ0c193aXRoKCJrYXBwYSIpKSAlPiUgZGF0YXRhYmxlKCkKY29lZmYudGJsIDwtIGRhdGF0YWJsZShjb2VmZl9saXZpbmcpCmBgYAoKCmBgYHtyIGV4YW1wbGUtY29lZmYtdGJsLCBlY2hvPUZBTFNFfQphY2NvcmRpb24ob3BlbiA9IEZBTFNFLCBjbGFzcyA9ICJicy1zZWNvbmRhcnkiLCBhY2NvcmRpb25fcGFuZWwoIkNsaWNrIHRvIHNlZSBkZW1vIHRhYmxlIHdpdGggZXhwZWN0ZWQgdmFsdWVzIiwgZXhhbXBsZSkpCmBgYAoKYGBge3IgY29lZmYtdGJsLCBlY2hvPUZBTFNFfQphY2NvcmRpb24ob3BlbiA9IEZBTFNFLCBjbGFzcyA9ICJicy1zZWNvbmRhcnkiLCBhY2NvcmRpb25fcGFuZWwoIkNsaWNrIHRvIHNlZSBvdXIgcmVzdWx0cyIsIGNvZWZmLnRibCkpCmBgYAoKIyMjIyBLaW5zaGlwIENvZWZmaWNpZW50cwoKSXQgaXMgbXVjaCBlYXNpZXIgdG8gdmlzdWFsaXplIHBhaXJ3aXNlIHJlbGF0aW9uc2hpcHMgYXMgbWF0cmljZXMgb3IgbXVsdGlkaW1lbnNpb25hbCBwbG90cy4gTGV0J3MgY3JlYXRlIHNvbWUgZ3JhcGhpY2FsIHZpc3VhbGl6YXRpb25zIHRvIGludGVycHJldCB0aGVzZSBzdGF0cy5cbgpcbgpUaGUgY29kZSBiZWxvdyB3aWxsIHByb2R1Y2UgYSBiYXNpYyB2aXN1YWxpemF0aW9uIG9mIHRoZSBvdmVyYWxsIHBhdHRlcm5zIGluIHJlbGF0ZWRuZXNzIGJldHdlZW4gYWxsIGxpdmluZyBpbmRpdmlkdWFscyBpbiB0aGUgY3VycmVudCBwb3B1bGF0aW9uLiAgRWFjaCBjZWxsIHdpbGwgYmUgY29sb3JlZCBieSB0aGUgcGFpcndpc2Uga2luc2hpcCBjb2VmZmljaWVudHMgd2UgY29tcHV0ZWQgYWJvdmUuCgpgYGB7ciBzZXR1cC1raW5zaGlwLWhlYXRtYXB9Cmtpbi5wbG90IDwtIG1hdHJpeC5oZWF0bWFwKGxpdmluZy5raW5zaGlwLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIktpbnNoaXAgQWNyb3NzIExpdmluZyBQb3B1bGF0aW9uIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJLaW5zaGlwIENvZWZmaWNpZW50cyIpCmBgYAoKYGBge3Iga2luc2hpcC1oZWF0bWFwLCBlY2hvPUZBTFNFfQphY2NvcmRpb24ob3BlbiA9IEZBTFNFLCBjbGFzcyA9ICJicy1zZWNvbmRhcnkiLCBhY2NvcmRpb25fcGFuZWwoIkNsaWNrIHRvIHNlZSBvdXRwdXQiLCBraW4ucGxvdCkpCmBgYAoKIyMjIE1hdGNoZWQgUGFpcnMgCgpGaW5hbGx5LCB3ZSB3YW50IHRvIGxvb2sgY2xvc2VyIGF0IHBvdGVudGlhbCBicmVlZGluZyBwYWlycyB0byBkbyBzb21lIG1hdGNoLW1ha2luZywgc28gbGV0J3MgZ2VuZXJhdGUgc29tZSBtb3JlIHBlZGlncmVlIHN0YXRpc3RpY3MgcGVyIGluZGl2aWR1YWwgYW5kIHRoZW4gdmlzdWFsaXplIGEgcGxvdCBvZiBicmVlZGluZyBwYWlycy4KCiMjIyMgS2luc2hpcCBDb2VmZmljaWVudHMKCmBgYHtyIHNldHVwLWJ0cC1oZWF0bWFwfQpraW5zaGlwX2J0cCAgPC0gc3Vic2V0X21hdHJpeF9saXZpbmcobGl2aW5nLmtpbnNoaXAsIHN0dWRib29rKQpidHAucGxvdCAgICAgPC0gbWF0cml4LmhlYXRtYXAoa2luc2hpcF9idHAsICJLaW5zaGlwIFZhbHVlcyBmb3IgUG90ZW50aWFsIE1hdGUgUGFpcnMiLCAiS2luc2hpcCBWYWxzIikKYGBgCgoKYGBge3IgYnRwLWhlYXRtYXAsICBlY2hvPUZBTFNFfQphY2NvcmRpb24ob3BlbiA9IEZBTFNFLCBjbGFzcyA9ICJicy1zZWNvbmRhcnkiLCBhY2NvcmRpb25fcGFuZWwoIkNsaWNrIHRvIHNlZSBvdXRwdXQiLCBidHAucGxvdCkpCmBgYAoKIyBTZWxlY3QgJiBWaXN1YWxpemUgTWF0Y2gKCk9uY2UgeW91IGNob29zZSBzb21lIHBvdGVudGlhbCBwYWlycywgeW91IHNob3VsZCBwbHVnIHRoZWlyIElEJ3MgaW50byB0aGUgY29kZSBjaHVua3MgYmVsb3cuXG4KXG4KWW91IG5lZWQgdG8gY2xpY2sgInJ1biBjb2RlIiB0byBsb2cgeW91ciBlbnRyeSBmb3Igc2VsZWN0ZWQgcGFpciBpZHMgYmVmb3JlIHByb2NlZWRpbmcuXG4KXG4KVGhpcyB3aWxsIGdpdmUgeW91IGEgZmV3IG1vcmUgZ3JhcGhpY3MgdG8gaW5zcGVjdCB0aGVpciByZWxhdGlvbnNoaXAuIFlvdSBjYW4gZWFzaWx5IHN3YXAgb3V0IGRpZmZlcmVudCBJRHMgYW5kIHJlLXJ1biB0aGUgY29kZSBjaHVua3MgdG8gc2VlIHRob3NlIHJldmlzZWQgcmVzdWx0cyB1bnRpbCB5b3UgZmluZCB0aGUgcGFpcnMgeW91IGFyZSBoYXBweSB3aXRoLgoKYGBge3Igc2V0dXAtcGFpcn0KbWFsZSAgIDwtIGMoIjI2NTIiKSAjIFJlcGxhY2UgdGhlIG51bWJlciBoZXJlIHdpdGggdGhlIElEIG51bWJlciBvZiB5b3VyIG1hbGUgb2YgaW50ZXJlc3QuCmZlbWFsZSA8LSBjKCIyNjc3IikgIyBSZXBsYWNlIHRoZSBudW1iZXIgaGVyZSB3aXRoIHRoZSBJRCBudW1iZXIgb2YgeW91ciBmZW1hbGUgb2YgaW50ZXJlc3QuCmBgYAoKCmBgYHtyIHBhaXJ9CnBhaXIgICA8LSBjKG1hbGUsIGZlbWFsZSkKYGBgCgojIyBJQkQgYW5kIEthcHBhIENvZWZmaWNpZW50cyAKCk5vdyB3ZSBhcmUgZ29pbmcgdG8gcHJpbnQgc29tZXRoaW5nIGNhbGxlZCBhIEthcHBhIFRyaWFuZ2xlLiBUaGlzIGlzIGEgd2F5IHRvIHZpc3VhbGl6ZSB0aGUgbW9zdCBwcm9iYWJsZSBpZGVudGl0eSBieSBkZXNjZW50IChJQkQpIGZvciB0aGlzIHBhaXIgaW4gYSAyLWRpbWVuc2lvbmFsIHNwYWNlLgoKYGBge3Igc2V0dXAta2FwcGF9CmthcHBhLnBhaXIgPC0gY29lZmZfbGl2aW5nICU+JQogIGZpbHRlcih4b3IoKGlkMSA9PSBtYWxlICAgJiBpZDIgPT0gZmVtYWxlKSwgCiAgICAgICAgICAgICAoaWQxID09IGZlbWFsZSAmIGlkMiA9PSBtYWxlKSkpICU+JQogIHNlbGVjdChpZDEsIGlkMiwga2FwcGEwLCBrYXBwYTEsIGthcHBhMikKa2FwcGEudGJsICA8LSBkYXRhdGFibGUoa2FwcGEucGFpcikKa2FwcGEudHJpIDwtIHNob3dJblRyaWFuZ2xlKGthcHBhLnBhaXIsIHBsb3RUeXBlID0gInBsb3RseSIpCmBgYAoKCmBgYHtyIGthcHBhLXJlc3VsdCwgZWNobz1GQUxTRX0KYWNjb3JkaW9uKG9wZW4gPSBGQUxTRSwgY2xhc3MgPSAiYnMtc2Vjb25kYXJ5IiwgYWNjb3JkaW9uX3BhbmVsKCJDbGljayB0byBzZWUgcmVzdWx0Iiwga2FwcGEudGJsKSkKYGBgCgoKYGBge3Iga2FwcGEsICBlY2hvPUZBTFNFfQphY2NvcmRpb24ob3BlbiA9IEZBTFNFLCBjbGFzcyA9ICJicy1zZWNvbmRhcnkiLCBhY2NvcmRpb25fcGFuZWwoIkNsaWNrIHRvIHNlZSBwbG90Iiwga2FwcGEudHJpKSkKYGBgCgpUaGUgcmVkIFggbWFyayByZXByZXNlbnRzIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB5b3VyIGh5cG90aGV0aWNhbCBwYWlyLCB3aGlsZSB0aGUgbGV0dGVycyBpbiB0aGUgZ3JhcGhpYyBhYm92ZSBhcmUgcmVmZXJlbmNlIHBvaW50cyBmb3Igd2hlcmUgZGlmZmVyZW50IHJlbGF0aW9uc2hpcHMgd291bGQgZmFsbCBpbiB0aGF0IHNwYWNlLiBUaGUgc3VtbWFyeSB0YWJsZSBiZWxvdyBjb250YWlucyBhIGtleSBmb3IgdGhvc2UgYWJicmV2aWF0aW9ucyAoYW5kIHRoZWlyIGFzc29jaWF0ZWQgdmFsdWVzIHBsb3R0ZWQgYWJvdmUpLgoKYGBge3IgcHJpbnQtZXhhbXBsZS1pYmQsIGVjaG89RkFMU0V9CmFjY29yZGlvbihvcGVuID0gRkFMU0UsIGNsYXNzID0gImJzLXNlY29uZGFyeSIsIGFjY29yZGlvbl9wYW5lbCgiRm9yIGhlbHAgaW50ZXJwcmV0aW5nIHRoZSBhYmJyZXZpYXRpb25zIGluIHRoZSBncmFwaGljIiwgZXhhbXBsZSkpCmBgYAoKIyMgQW5ub3RhdGVkIFN1YnBsb3RzIGZvciBNYXRjaCAKCk5vdyB3ZSBjYW4gcmVjb25zdHJ1Y3Qgb3VyIG5ldHdvcmsgdmlzdWFsaXphdGlvbnMgd2l0aCBjbGVhcmVyIGFubm90YXRpb25zIHRvIGZvY3VzIG9uIG91ciBwcm9wb3NlZCBwYWlyLgoKPlJlY2FsbCB0aGF0IHRoZXNlIHBsb3RzIGNhbiBiZSBzbG93ZXIgdG8gbG9hZCwgYnV0IHRoZXkgc2hvdWxkIGJlIHF1aWNrZXIgbm93IHRoYXQgd2UgYXJlIG9ubHkgd29ya2luZyB3aXRoIGEgc3VicGxvdC4KCkFsc28sIG5vdGUgdGhhdCB0aGUgc3VidGl0bGUgb2YgdGhlc2UgcGxvdHMgbm93IGNvbnRhaW5zIGEgcHJpbnRlZCBzdW1tYXJ5IHByb2R1Y2VkIGJ5IHRoZSBgdmVyYmFsaXNlYCBwYWNrYWdlIHRoYXQgaXMgcGFydCBvZiBgcGVkc3VpdGVgLgoKCmBgYHtyIHNldHVwLXBhaXJuZXR9CnBhaXIubmV0ICA8LSBzdWJzZXRfbmV0d29ya19idHAocGVkLmxpdmluZywgY29sb3JzLCBwYWlyLCBzdHVkYm9vaykKcGFpci52ZXJ0IDwtIHBhaXIubmV0ICU+JSB2aXNIaWVyYXJjaGljYWxMYXlvdXQoZGlyZWN0aW9uID0gIlVEIiwgc2hha2VUb3dhcmRzID0gInJvb3RzIikKYGBgCgpgYGB7ciBwbG90LXBhaXJ2ZXJ0LCAgZWNobz1GQUxTRX0KYWNjb3JkaW9uKG9wZW4gPSBGQUxTRSwgY2xhc3MgPSAiYnMtc2Vjb25kYXJ5IiwgYWNjb3JkaW9uX3BhbmVsKCJDbGljayB0byBzZWUgZ3JhcGhpYyIsIHBhaXIudmVydCkpCmBgYAoKYGBge3IgcGxvdC1wYWlybmV0LCAgZWNobz1GQUxTRX0KYWNjb3JkaW9uKG9wZW4gPSBGQUxTRSwgY2xhc3MgPSAiYnMtc2Vjb25kYXJ5IiwgYWNjb3JkaW9uX3BhbmVsKCJDbGljayB0byBzZWUgZ3JhcGhpYyIsIHBhaXIubmV0KSkKYGBgCgoKIyBNYWtlIHlvdXIgUGxhbiBhbmQgUmVwb3J0IAoKTm93IHlvdSBzaG91bGQgdXNlIGFsbCB0aGUgaW5mb3JtYXRpb24gcHJvdmlkZWQgdG8gbWF0Y2ggYXQgbGVhc3Qgb25lIGJyZWVkaW5nIHBhaXIgZm9yIHlvdXIgbmV3IGV4aGliaXQgYW5kIGFkZCBhIHBhcmFncmFwaCBpbiB3aGljaCB5b3UgZXhwbGFpbiB0aGlzIGNob2ljZSBhbmQgcGxhbi4gCgotIFJ1biBlYWNoIGNodW5rIGJlbG93IHRvIHJlbmRlciBhIHBsb3Qgb3IgdGFibGUgY3JlYXRlZCBhYm92ZS4KLSBSZWFkIHRoZSBleGNlcnB0IGZyb20gYW4gZXhhbXBsZSBCVFAgYW5kIHdyaXRlIHlvdXIgb3duIHZlcnNpb24gb2YgYSByZWZsZWN0aW9uIG9uIHRoZSByZXN1bHRzIHNob3duIGluIHRoYXQgZmlndXJlL3RhYmxlLgogIC0gKllvdXJzIGRvZXMgbm90IG5lZWQgdG8gYmUgYXMgcHJlY2lzZSBvciBkZXRhaWxlZCBhcyBtYW55IG9mIHRoZSBleGFtcGxlcyBwcm92aWRlZC4gSnVzdCB1c2UgdGhlIHRleHQgdG8gZ3VpZGUgb25lIG9yIHR3byBzZW50ZW5jZXMgYWJvdXQgZWFjaCBpdGVtLioKLSBPbmNlIHlvdSBoYXZlIGFuc3dlcmVkIGVhY2ggb2YgdGhlc2UgcXVlc3Rpb25zLCB5b3Ugc2hvdWxkIGNsaWNrIHRoZSBgS25pdGAgYnV0dG9uIGF0IHRoZSB0b3Agb2YgdGhpcyBwYW5lbCBhbmQgd2FpdCBmb3IgUiBTdHVkaW8gdG8gY29udmVydCB5b3VyIGRvY3VtZW50IGludG8gYW4gaHRtbCByZXBvcnQuCiAgLSBUaGlzIGlzIHlvdXIgZ3JvdXAncyBmaW5hbGl6ZWQgKipCcmVlZGluZyBhbmQgVHJhbnNmZXIgUGxhbioqLgogIC0gWW91IHdpbGwgbG9hZCB5b3VyIGh0bWwgZmlsZSBpbnRvIHRoZSBhc3NpZ25tZW50IHN1Ym1pc3Npb24gcG9ydGFsIG9uIGNhbnZhcy4KLSBTdG9yZSB0aGUgaW1hZ2UgZmlsZXMgY3JlYXRlZCBpbiBhIGxvY2F0aW9uIHdoZXJlIHlvdSBjYW4gYWNjZXNzIHRoZW0gbGF0ZXIgZm9yIHlvdXIgb25saW5lIHBvcnRmb2xpby4KICAtIFlvdXIgd29ya2luZyBkaXJlY3Rvcnkgd2lsbCBjb250YWluIGEgc2VyaWVzIG9mIGZpbGVzIGVuZGluZyBpbiBgLmh0bWxgLCBgLnBuZ2AsIG9yIGAuY3N2YC4gWW91IG1heSBmaW5kIGFueSBvZiB0aGVzZSB1c2VmdWwgbGF0ZXIuCiAgLSAqTm90ZSB0aGF0IHRvIHZpZXcgdGhlIGh0bWwgZmlsZXMsIHlvdSBzaG91bGQgZG91YmxlIGNsaWNrIHRoZSBmaWxlIHRvIG9wZW4gaXQgaW4gYW4gaW50ZXJuZXQgYnJvd3Nlci4qCgojIyBEZW1vZ3JhcGh5IAoKIyMjIEV4YW1wbGVzCiAKPlRoZSBQcm9zaW1pYW4gVEFHIGhhcyBzZXQgYSB0YXJnZXQgcG9wdWxhdGlvbiBzaXplIG9mIDU1IGFuaW1hbHMgaW4gdGhlIFB5Z215IFNsb3cgTG9yaXMgU1NQIHBvcHVsYXRpb24uIFRoZSBtYW5hZ2VkIHBvcHVsYXRpb24gaGFzIGJlZW4gaW5jcmVhc2luZyAozrsgPSAwLjk2KSBoaXN0b3JpY2FsbHksIGFuZCBoYXMgcmV0YWluZWQgOTIuMzklIG9mIGl0cyBmb3VuZGluZyBnZW5lIGRpdmVyc2l0eS4KCgo+VGhpcyBTU1Agc3BlY2llcyBmaXJzdCBhcHBlYXJlZCBpbiBBWkEgZmFjaWxpdGllcyBpbiAxOTY4IHdoZW4gYSBzaW5nbGUgbWFsZSB3YXMgY29uZmlzY2F0ZWQgYW5kIHRyYW5zZmVycmVkIHRvIHRoZSBIb25vbHVsdSBab28uIEZyb20gMTk2OCDigJMgMTk4NiwgdGhlIHBvcHVsYXRpb24gc2l6ZSByZW1haW5lZCBsb3csIG5ldmVyIGV4Y2VlZGluZyBmb3VyIGluZGl2aWR1YWxzLCBhbmQgdGhlIEhvbm9sdWx1IFpvbyByZW1haW5lZCB0aGUgb25seSBob2xkaW5nIGluc3RpdHV0aW9uLiBUaGUgY3VycmVudCBTU1AgcG9wdWxhdGlvbiB3YXMgZm91bmRlZCBpbiAxOTg3IHdoZW4gdGhlIFNhbiBEaWVnbyBab28sIER1a2UgTGVtdXIgQ2VudGVyLCBhbmQgQ2luY2lubmF0aSBab28gaW1wb3J0ZWQgMjkgaW5kaXZpZHVhbHMgZnJvbSBTd2VkZW4uIFRoZSBmaXJzdCByZWNvcmRlZCBiaXJ0aHMgb2NjdXJyZWQgaW4gMTk4OCBhdCBhbGwgdGhyZWUgZmFjaWxpdGllcyB0aGF0IHdvcmtlZCB0byBpbXBvcnQgYW5pbWFscy4gVGhlIHBvcHVsYXRpb24gc3RlYWRpbHkgZ3JldyB0byBhIHBlYWsgb2YgNzYgaW5kaXZpZHVhbHMgYnkgMjAxMSAoRmlndXJlIDEpLiBUaGlzIGdyb3d0aCBjYW4gbGFyZ2VseSBiZSBhdHRyaWJ1dGVkIHRvIHN1Y2Nlc3NmdWwgYnJlZWRpbmcgYW5kIHNlY29uZGx5IHRvIGEgc21hbGwgbnVtYmVyIG9mIGNvbnRpbnVlZCBpbXBvcnRzLiBTaW5jZSAyMDEyLCB0aGUgcG9wdWxhdGlvbiBoYXMgZXhwZXJpZW5jZWQgYSBub3RhYmxlIGRlY2xpbmUgaW4gc2l6ZSBwcmltYXJpbHkgZHVlIHRvIGluc3VmZmljaWVudCByZXByb2R1Y3Rpb24uIFRoZSByZWFzb25zIGZvciB0aGlzIGxvdywgaW5jb25zaXN0ZW50IHJlcHJvZHVjdGlvbiBpbiByZWNlbnQgeWVhcnMgYXJlIGN1cnJlbnRseSB1bmNsZWFyLCBidXQgbWF5IGJlIGFzc29jaWF0ZWQgd2l0aCBodXNiYW5kcnksIHBhcnRpY3VsYXJseSBkaWV0LiBIb3dldmVyLCB0aGUgcG9wdWxhdGlvbiBoYXMgc2hvd24gZ3Jvd3RoIG92ZXIgdGhlIGxhc3QgZml2ZSB5ZWFycyBhZ2FpbiBhbmQgdGhlcmUgYXJlIGVub3VnaCBiaXJ0aHMgdG8gb2Zmc2V0IGRlYXRocyBpbiB0aGUgcG9wdWxhdGlvbi4KCiMjIyBSZXN1bHRzCgpgYGB7ciByZXZpZXctcmVzdWx0cy1kZW0sIGVjaG89RkFMU0V9CmFjY29yZGlvbihvcGVuICA9IEZBTFNFLCAKICAgICAgICAgIGNsYXNzID0gImJzLXNlY29uZGFyeSIsIAogICAgICAgICAgdGl0bGUgPSAiUmV2aWV3IEtleSBEZW1vZ3JhcGhpYyBSZXN1bHRzIGZvciBJbnRlcnByZXRhdGlvbiIsCiAgICAgICAgICBhY2NvcmRpb25fcGFuZWwoIlN1bW1hcnkgVGFibGUgLSBGb3VuZGVycyIgICAgICAgLCBmb3VuZGVyLnZpcyksCiAgICAgICAgICBhY2NvcmRpb25fcGFuZWwoIlBsb3QgLSBQb3B1bGF0aW9uIEdyb3d0aCIgICAgICAgLCBsYW1iZGEucGxvdCksCiAgICAgICAgICBhY2NvcmRpb25fcGFuZWwoIlN1bW1hcnkgVGFibGUgLSBLaW5zaGlwIE1ldHJpY3MiLCBraW5zaGlwLnN1bW1hcnkpCiAgICAgICAgICApCmBgYAoKIyMjIFN1bW1hcnkKCkFkZCB5b3VyIHRleHQgaGVyZS4KCiMjIEdlbmV0aWNzIAoKIyMjIEV4YW1wbGVzCgo+R2VuZXRpYyB2YWx1ZXMgYXJlIGNhbGN1bGF0ZWQgYWZ0ZXIgaW5jb3Jwb3JhdGluZyBwZWRpZ3JlZSBhc3N1bXB0aW9ucyBhbmQgcmVtb3ZpbmcgZXhjbHVkZWQgaW5kaXZpZHVhbHMuIEFuYWx5c2lzIG9mIHRoZSBzdHVkYm9vayBpbmRpY2F0ZXMgdGhhdCB0aGlzIFNTUCBpcyBkZXNjZW5kZWQgZnJvbSAzMCBmb3VuZGVycyB3aXRoIG5vIHBvdGVudGlhbCBmb3VuZGVycyByZW1haW5pbmcgKEZpZ3VyZSAzLCBUYWJsZSAyKS4gVGhlIGdlbmUgZGl2ZXJzaXR5IG9mIHRoZSBwb3B1bGF0aW9uIGlzIDkyJS4gQmFzZWQgb24gY3VycmVudCBmb3VuZGVyIHJlcHJlc2VudGF0aW9ucywgZ2VuZSBkaXZlcnNpdHkgaXMgZXF1aXZhbGVudCB0byB0aGF0IGZvdW5kIGluIGFwcHJveGltYXRlbHkgc2l4IGZvdW5kZXJzLiBUaGUgY3VycmVudCBtZWFuIGtpbnNoaXAgaW4gdGhlIHBvcHVsYXRpb24gaXMgMC4wNzk5OyBmaXJzdC1jb3VzaW5zIGhhdmUgYSBraW5zaGlwIG9mIDAuMDYyNSwgd2hpY2ggbWVhbnMgdGhhdCB0aGUgYXZlcmFnZSByZWxhdGlvbnNoaXAgaW4gdGhlIHBvcHVsYXRpb24gaXMgc2xpZ2h0bHkgY2xvc2VyIHRoYW4gdGhhdCBvZiBub25pbmJyZWQgZmlyc3QtY291c2lucy4KCj5Qb3B1bGF0aW9uIG1hbmFnZW1lbnQgdGhlb3J5IHN1Z2dlc3RzIGdlbmV0aWMgbWFuYWdlbWVudCBzaG91bGQgc3RyaXZlIHRvIG1haW50YWluIHRocmVzaG9sZHMgZm9yIHRvbGVyYW5jZSBvZiBnZW5lIGRpdmVyc2l0eSBsb3NzLiBUaGUgc3RhbmRhcmQgZ29hbCBpcyA5MCUgZ2VuZSBkaXZlcnNpdHkgcmV0ZW50aW9uIGZvciAxMDAgeWVhcnMuIERlY3JlYXNlcyBpbiBnZW5lIGRpdmVyc2l0eSBiZWxvdyA5MCUgb2YgdGhhdCBpbiB0aGUgZm91bmRpbmcgcG9wdWxhdGlvbiBoYXZlIGJlZW4gYXNzb2NpYXRlZCB3aXRoIGluY3JlYXNpbmdseSBjb21wcm9taXNlZCByZXByb2R1Y3Rpb24gYnksIGFtb25nIG90aGVyIGZhY3RvcnMsIGxvd2VyIGJpcnRoL2hhdGNoIHdlaWdodHMsIHNtYWxsZXIgbGl0dGVyL2NsdXRjaCBzaXplcywgYW5kIGdyZWF0ZXIgbmVvbmF0YWwgbW9ydGFsaXR5IGluIHNvbWUgc3BlY2llcy4KCj5CYXNlZCBvbiBjdXJyZW50IHBvcHVsYXRpb24gcGFyYW1ldGVycywgZ2VuZSBkaXZlcnNpdHkgaXMgcHJvamVjdGVkIHRvIGRlY2xpbmUgdG8gNjYlIG92ZXIgdGhlIG5leHQgMTAwIHllYXJzIGlmIHRoZSBjdXJyZW50IHBvcHVsYXRpb24gZ3Jvd3MgdG8gdGhlIHJlY29tbWVuZGVkIHRhcmdldCBzaXplIG9mIDU1IChUYWJsZSAzLCBTY2VuYXJpbyBhKS4gVGhlIG1vc3QgZWZmZWN0aXZlIHdheXMgdGhpcyBwb3B1bGF0aW9uIGNhbiBtYWludGFpbiBtb3JlIGdlbmUgZGl2ZXJzaXR5IGFyZSB0byBoYXZlIGFuIGluY3JlYXNpbmcgZ3Jvd3RoIHJhdGUgKHZzLiBzdGFibGUpIGFuZCBhIGxhcmdlciBsb25nLXRlcm0gcG9wdWxhdGlvbiBzaXplLiBUaGUgZWZmZWN0aXZlIHBvcHVsYXRpb24gc2l6ZSBpcyBoaWdoIGFuZCBpcyBoZWxwaW5nIHRvIG1haW50YWluIGdlbmUgZGl2ZXJzaXR5IGluIHRoaXMgcG9wdWxhdGlvbi4KCiMjIyBSZXN1bHRzCgpgYGB7ciByZXZpZXctcmVzdWx0cy1wZWQsIGVjaG89RkFMU0V9CmFjY29yZGlvbihvcGVuICA9IEZBTFNFLCAKICAgICAgICAgIGNsYXNzID0gImJzLXNlY29uZGFyeSIsIAogICAgICAgICAgdGl0bGUgPSAiUmV2aWV3IFBlZGlncmVlIFJlc3VsdHMgZm9yIEludGVycHJldGF0aW9uIiwKICAgICAgICAgIGFjY29yZGlvbl9wYW5lbCgiQmFzaWMgUGxvdCIgICAgICAgICAgICAgICAgICAgICAgICwgcGVkX2xpdmluZ19wbG90KSwKICAgICAgICAgIGFjY29yZGlvbl9wYW5lbCgiUGVkaWdyZWUgTmV0d29yayIgICAgICAgICAgICAgICAgICwgcGVkLm5ldCksCiAgICAgICAgICBhY2NvcmRpb25fcGFuZWwoIktpbnNoaXAgSGVhdG1hcCAtIFBvdGVudGlhbCBQYWlycyIsIGJ0cC5wbG90KQogICAgICAgICAgKQpgYGAKCgojIyMgU3VtbWFyeQoKQWRkIHlvdXIgdGV4dCBoZXJlLgoKIyMgUmVjb21tZW5kYXRpb25zIAoKIyMjIEV4YW1wbGVzCgpUYWJsZTogKipOWSBCUk9OWCoqPGJyPioqQnJvbnggWm9vL1dpbGRsaWZlIENvbnNlcnZhdGlvbiBTb2NpZXR5KioKCnxTQiBJRCB8TG9jYWwgSUR8U2V4ICAgfEFnZSAgfERpc3Bvc2l0aW9uICB8TG9jYXRpb24gICB8QnJlZWRpbmcgICAgICB8IFdpdGggICB8Cnw6LS0tLTp8Oi0tLS0tLTp8Oi0tLS0tfDotLS06fDotLS0tLS0tLS0tLTp8Oi0tLS0tLS0tLTp8Oi0tLS0tLS0tLS0tLTp8Oi0tLS0tLTp8CnwyNjIzICB8TTE0MDUyICB8TSAgICAgfDE0ICAgfEhPTEQgICAgICAgICB8TlkgQlJPTlggICB8QlJFRUQgV0lUSCAgICB8MjY0MCAgICB8CnwyNjQwICB8TTE0MDUzICB8RiAgICAgfDEyICAgfEhPTEQgICAgICAgICB8TlkgQlJPTlggICB8QlJFRUQgV0lUSCAgICB8MjYyMyAgICB8CnwyNjgyICB8TTE5MDA3ICB8TSAgICAgfDcgICAgfFJFQ0VJVkUgRlJPTSB8QkxPT01JTkdUICB8QlJFRUQgV0lUSCAgICB8MjcwOCAgICB8CnwyNzA4ICB8MTE2MjgzICB8RiAgICAgfDQgICAgfFJFQ0VJVkUgRlJPTSB8TlpQLVdBU0ggICB8QlJFRUQgV0lUSCAgICB8MjY4MiAgICB8CgpUYWJsZTogKipOWlAtV0FTSCoqPGJyPioqU21pdGhzb25pYW4gTmF0aW9uYWwgWm9vbG9naWNhbCBQYXJrKioKCnxTQiBJRCB8TG9jYWwgSUR8U2V4ICAgfEFnZSAgfERpc3Bvc2l0aW9uICB8TG9jYXRpb24gICB8QnJlZWRpbmcgICAgICB8IFdpdGggICB8Cnw6LS0tLTp8Oi0tLS0tLTp8Oi0tLS0tfDotLS06fDotLS0tLS0tLS0tLTp8Oi0tLS0tLS0tLTp8Oi0tLS0tLS0tLS0tLTp8Oi0tLS0tLTp8CnwyNzA4ICB8MTE2MjgzICB8NCAgICAgfDE0ICAgfFNFTkQgVE8gICAgICB8TlkgQlJPTlggICB8QlJFRUQgV0lUSCAgICB8MjY4MiAgICB8CnwyNzE1ICB8MTE2MjgyICB8MyAgICAgfDEyICAgfEhPTEQgICAgICAgICB8TlpQLVdBU0ggICB8RE8gTk9UIEJSRUVEICB8ICAgICAgICB8CnwyNzM1ICB8MTE2NDYxICB8MCAgICAgfDcgICAgfEhPTEQgICAgICAgICB8TlpQLVdBU0ggICB8RE8gTk9UIEJSRUVEICB8ICAgICAgICB8CnwyNzM2ICB8MTE2NDYyICB8MCAgICAgfDQgICAgfEhPTEQgICAgICAgICB8TlpQLVdBU0ggICB8RE8gTk9UIEJSRUVEICB8ICAgICAgICB8CgpUYWJsZTogKipPTUFIQSoqPGJyPioqT21haGEncyBIZW5yeSBEb29ybHkgWm9vKioKCnxTQiBJRCB8TG9jYWwgSUR8U2V4ICAgfEFnZSAgfERpc3Bvc2l0aW9uICB8TG9jYXRpb24gICB8QnJlZWRpbmcgICAgICB8IFdpdGggICB8Cnw6LS0tLTp8Oi0tLS0tLTp8Oi0tLS0tfDotLS06fDotLS0tLS0tLS0tLTp8Oi0tLS0tLS0tLTp8Oi0tLS0tLS0tLS0tLTp8Oi0tLS0tLTp8CnwyNjUyICB8MjQ0ODUgICB8TSAgICAgfDExICAgfEhPTEQgICAgICAgICB8T01BSEEgICAgICB8QlJFRUQgV0lUSCAgICB8MjY3NyAgICB8CnwyNjc3ICB8MjQ3MDkgICB8RiAgICAgfDcgICAgfEhPTEQgICAgICAgICB8T01BSEEgICAgICB8QlJFRUQgV0lUSCAgICB8MjY1MiAgICB8CgoKIyMjIFJlc3VsdHMKCmBgYHtyIHJldmlldy1yZXN1bHRzLWJ0cCwgZWNobz1GQUxTRX0KYWNjb3JkaW9uKG9wZW4gID0gRkFMU0UsIAogICAgICAgICAgY2xhc3MgPSAiYnMtc2Vjb25kYXJ5IiwgCiAgICAgICAgICB0aXRsZSA9ICJSZXZpZXcgTWV0cmljcyB0byBWYWxpZGF0ZSB5b3VyIEJUUCIsCiAgICAgICAgICBhY2NvcmRpb25fcGFuZWwoIkthcHBhIFRyaWFuZ2xlIiAgICAgICAgICAgICAgICAgICAgICwga2FwcGEudHJpKSwKICAgICAgICAgIGFjY29yZGlvbl9wYW5lbCgiU2hhcmVkIFBlZGlncmVlIEFuY2VzdHJ5IiAgICAgICAgICAgLCBwYWlyLnZlcnQpLAogICAgICAgICAgYWNjb3JkaW9uX3BhbmVsKCJTdW1tYXJ5IFRhYmxlIG9mIEN1cnJlbnQgUG9wdWxhdGlvbiIsIGxpdmluZy5zdW1tYXJ5KQogICAgICAgICAgKQpgYGAKCgoKIyMjIFN1bW1hcnkKCj4qKk5vdGUqKjogRm9yIHlvdXIgc3VtbWFyeSB5b3Ugc2hvdWxkIHdyaXRlIGEgZmV3IHNlbnRlbmNlcyBleHBsYWluaW5nIHRoZSBjaG9pY2UgeW91IG1hZGUuIFlvdSBtYXkgdXNlIGEgdGFibGUgZm9ybWF0IGxpa2UgdGhpcywgYSBidWxsZXRlZCBsaXN0LCBvciBhIHBhcmFncmFwaCB0byBwcmVzZW50IHlvdXIgcHJvcG9zZWQgYnJlZWRpbmcgcGFpcihzKS4KCkFkZCB5b3VyIHRleHQgaGVyZS4KCgoK